<?php

namespace Forxx\console;

use Forxx\Api\Utils;

class CConsole
{

	private static $_ARGS = [];

    public static $debugMode = false;
	/**
	 * 输入交互
	 * @param unknown $message
	 * @param string $default
	 * @return boolean|string
	 */
	public static function prompt($message, $default = null)
	{
		if ($default !== null)
			$message .= " \tdefault:[$default] ";
		else
			$message .= ' ';

		if (extension_loaded('readline')) {
			$input = readline($message);
			if ($input !== false)
				readline_add_history($input);
		} else {
			echo $message;
			$input = fgets(STDIN);
		}

		if ($input === false)
			return false;
		else {
			$input = trim($input);
			return ($input === '' && $default !== null) ? $default : $input;
		}
	}

	/**
	 * 确认交互
	 * @param unknown $message
	 * @param bool $default
	 * @return Ambigous <boolean, string>
	 */
	public static function confirm($message, $default = false)
	{
		echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';

		$input = trim(fgets(STDIN));
		return empty($input) ? $default : !strncasecmp($input, 'y', 1);
	}

	/**
	 * 选择交互
	 * @param $lists
	 * @param string $message
	 * @param int $default
	 * @return int|mixed|string
	 */
	public static function choice($lists, $message = 'do you choice :', $default = 0)
	{
		self::print_line($message);
		foreach ($lists as $key => $value) {
			self::print_line("\t{$key} : $value");
		}
		echo "your choice :  ";
		if (isset($lists[$default])) {
			echo "(default:{$lists[$default]})  ";
		} elseif (is_string($default)) {
			echo "(default:{$default})  ";
		}
		$choice = trim(fgets(STDIN));
		if (empty($choice)) {
			$choice = $default;
		}
		$keys = array_keys($lists);
		if (($res = array_search($choice, $lists)) !== false) {
			return $res;
		}
		if (in_array($choice, $keys)) {
			return $choice;
		}
		self::print_line("wrong choice!\n");
		return self::choice($lists, $message, $default);
	}

	/**
	 * 格式化输出
	 * @param string $string_format
	 * @param array $params
	 */
	public static function print_line($string_format, $params = array())
	{
		if (count($params) < 1) {
			echo $string_format;
		} else {
			echo str_replace(array_keys($params), array_values($params), $string_format);
		}
		echo "\n";
	}

    /**
	 * 格式化输出 仅debug打开时
	 * @param string $string_format
	 * @param array $params
     */
    public static function debug_print_line($string_format, $params = array()){
        if(self::$debugMode && defined("L_DEBUG") && L_DEBUG){
            self::print_line($string_format, $params);
        }
    }

	public static function run($args = [])
	{
		self::$_ARGS = $args;
		if (count($args) < 1) {
			self::get_help();
		} else {
			$base_class_name = array_shift($args);
			$class_name = "\\App\\CLI\\" . $base_class_name;
			self::try_to_include($base_class_name);
			if (class_exists($class_name)) {
				$runner = new $class_name();
				if (!($runner instanceof CLIRunner)) {
					self::print_line("command `{{cmd}}` not support!", array('{{cmd}}' => $class_name));
					self::get_help();
				} else {
					$runner->run($args);
				}
			} else {
				self::print_line("command `{{cmd}}` not found!", array('{{cmd}}' => $class_name));
				self::get_help();
			}
		}
	}

	public static function get_help()
	{
		self::print_line("Usage : php cli [cmd]");
		$commands = self::get_commands();
		asort($commands);
		$cmd_str = implode("\n\t ", $commands);
		self::print_line("can used cmd :\t\n\t {$cmd_str}");
	}

	public static function get_commands()
	{
		$commands = [];
		$path = L_APP_PATH . "CLI";
		$files = @scandir($path);
		if (!is_array($files) || count($files) < 1) {
			return $commands;
		}
		foreach ($files as $file) {
			$command = self::get_base_file_name($file);
			if ($command) {
				$commands[] = $command;
			}
		}
		return $commands;
	}

	public static function get_base_file_name($full_name)
	{
		if (strlen($full_name) < 5 || substr($full_name, -4) != ".php") {
			return false;
		}
		return substr($full_name, 0, strlen($full_name) - 4);
	}

	public static function try_to_include($class)
	{
		$file_path = L_APP_PATH . "CLI/" . $class . ".php";
		if (file_exists($file_path)) {
			require_once($file_path);
		}
	}

	/**
	 * 终止进程
	 * @param $pid
	 */
	public static function killProcess($pid)
	{
		if (Utils::isWin()) {
			system("taskkill /PID {$pid} /F /T");
		} else {
			system("kill -9 {$pid}");
		}
	}

	/**
	 * 进去任务
	 * @param string $taskName
	 * @param bool $forceIn
	 */
	public static function entryTask($forceIn = false, $taskName = '')
	{
		if (empty($taskName)) {
			$callList = debug_backtrace();
			$callClass = str_replace("\\", ".", isset($callList[1]['class']) ? $callList[1]['class'] : "");
			$callMethod = isset($callList[1]['function']) ? $callList[1]['function'] : "";
			$taskName = $callClass . " " . $callMethod;
		}
		$dir = L_WORKSPACE_PATH . "runtime" . DIRECTORY_SEPARATOR;
		if (!is_dir($dir)) {
			mkdir($dir, 0777, true);
		}
		$ret = true;
		if (file_exists($dir . $taskName)) {
			$ret = false;
			$content = file_get_contents($dir . $taskName);
			$lastProcessInfo = @json_decode($content, true);
			if (isset($lastProcessInfo['pid']) && $forceIn) {
				self::killProcess($lastProcessInfo['pid']);
			}
		}
		$pid = getmypid();
		file_put_contents($dir . $taskName, json_encode(['pid' => $pid]));
		return $ret;
	}

	/**
	 * 离开任务
	 */
	public static function leaveTask($taskName = '')
	{
		if (empty($taskName)) {
			$callList = debug_backtrace();
			$callClass = str_replace("\\", ".", isset($callList[1]['class']) ? $callList[1]['class'] : "");
			$callMethod = isset($callList[1]['function']) ? $callList[1]['function'] : "";
			$taskName = $callClass . " " . $callMethod;
		}
		$dir = L_WORKSPACE_PATH . "runtime/";
		if (file_exists($dir . $taskName)) {
			unlink($dir . $taskName);
		}
	}
}

abstract class CLIRunner
{
	protected function get_useful_actions()
	{
		$res = [];
		$reflection = new \ReflectionClass($this);
		$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
		foreach ($methods as $method) {
			$name = $method->name;
			$tmp = substr($name, -7, 7);
			if ($tmp == '_action') {
				$res[] = substr($name, 0, strlen($name) - 7);
			}
		}
		return $res;
	}

	public function get_help()
	{
		CConsole::print_line("can used action:");
		$actions = $this->get_useful_actions();
		asort($actions);
		CConsole::print_line("-- can used action: \t\n {{action}}", array("{{action}}" => implode(" \t\n ", $actions)));
	}

	public function run($args = [])
	{
		if (count($args) < 1) {
			$this->get_help();
		} else {
			$action = array_shift($args);
			$method = $action . "_action";
			if (method_exists($this, $method)) {
				$res = call_user_func_array(array($this, $method), $args);
				echo json_encode($res, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
				echo "\n";
			} else {
				CConsole::print_line("can not found `{{method}}` action!", array('{{method}}' => $action));
			}
		}
	}
}
